﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Web;
using SocialLearning.CoreInterfaces;
using SocialLearning;

namespace ArchitectureV2
{
    public class Core : I_CMA_Core
    {
        #region Nested Classes

        class Dispatcher
        {
            #region Fields

            private Queue<byte[]> packets;
            private EventWaitHandle packetReady;
            private Dictionary<object, EndPoint> destination;
            private Thread thread;
            private bool done;

            #endregion

            #region Constructor

            public Dispatcher(Dictionary<object, EndPoint> destination)
            {
                this.destination = destination;
                packetReady = new EventWaitHandle(false, EventResetMode.AutoReset);
                packets = new Queue<byte[]>();
            }

            #endregion

            #region Public Methods

            public void EnqueuePacket(byte[] packet)
            {
                lock (packets)
                {
                    packets.Enqueue(packet);
                    packetReady.Set();
                    if (thread == null) { StartThread(); }
                }
            }

            public void StartThread()
            {
                lock (packets) { if (thread != null) { return; } }
                StopThread();
                done = false;
                thread = new Thread(new ThreadStart(SendPackets));
                thread.Start();
            }

            public void StopThread()
            {
                lock (packets) { if (thread == null) { return; } }
                done = true;
                packetReady.Set();
                thread.Join();
                thread = null;
            }

            #endregion

            #region Dispatcher Thread

            private void SendPackets()
            {
                Socket socket;
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                while (true)
                {
                    byte[] data = null;
                    lock (packets)
                    {
                        if (done) { break; }
                        if (packets.Count > 0) { data = packets.Dequeue(); }
                    }
                    if (data == null)
                    {
                        packetReady.WaitOne();
                        continue;
                    }
                    lock (destination)
                    {
                        foreach (KeyValuePair<object, EndPoint> pair in destination)
                        {
                            try { socket.SendTo(data, pair.Value); }
                            catch { }
                        }
                    }
                }
                socket.Dispose();
            }

            #endregion
        }

        #endregion

        #region Fields

        private static Dispatcher dispatcher;
        private static Dictionary<object, EndPoint> users;

        private UserSession server;

        #endregion

        #region Constructor

        static Core()
        {
            users = new Dictionary<object, EndPoint>();
            dispatcher = new Dispatcher(users);
        }

        public Core(UserSession server) { this.server = server; }

        #endregion

        #region Properties

        public int UserCount { get { return (users.Count); } }
        public I_UAC_Account User { get { return (server.User); } }

        #endregion

        #region Public Methods

        public void Register(object key, EndPoint ip)
        {
            if (key == null) { return; }
            lock (users) { users[key] = ip; }
        }

        public void Deregister(object key)
        {
            if (key == null) { return; }
            lock (users) { if (users.ContainsKey(key)) { users.Remove(key); } }
        }

        #endregion

        #region I_CMA_Core

        public void BroadcastPing(int pingType, string pingData)
        {
            char[] a = null; if (pingData != null) { a = pingData.ToCharArray(); }
            int len = (a == null) ? 0 : a.Length;
            if (len > (512 - 4)) { len = 512 - 4; }
            byte[] data = new byte[len + 4];
            data[0] = (byte)((pingType >> 24) & 0xff);
            data[1] = (byte)((pingType >> 16) & 0xff);
            data[2] = (byte)((pingType >> 8) & 0xff);
            data[3] = (byte)(pingType & 0xff);
            if (a != null) { for (int i = 0; i < len; i++) { data[i + 4] = (byte)(a[i] & 0xff); } }
            dispatcher.EnqueuePacket(data);
        }
        
        public object InvokeMethod(string module, string method, object[] parameters)
        {
            return (server.InvokeMethod(module, method, parameters));
        }

        public string[] GetModuleList() { return (server.GetModuleList()); }

        public string[] GetMethodList(string module) { return (server.GetMethodList(module)); }

        #endregion
    }
}